package com.atguigu.spzx.log.aspect;

import com.alibaba.fastjson.JSON;
import com.atguigu.spzx.log.anno.SystemOperLog;
import com.atguigu.spzx.log.service.SysOperLogService;
import com.atguigu.spzx.util.IpUtil;
import com.atguigu.spzx.model.entity.system.SysOperLog;
import com.atguigu.spzx.model.entity.system.SysUser;
import com.atguigu.spzx.model.exception.GuiguException;
import com.atguigu.spzx.model.vo.common.ResultCodeEnum;
import com.atguigu.spzx.util.AuthContextUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Date;

/**
 * @author LT
 * @create 2024-03-20-20:19
 */
@Aspect
@Slf4j
public class SpzxLogAspect {

    @Resource
    SysOperLogService service;

    @Around(value = "@annotation(systemOperLog)")//使用方法的形参作为切入点
    public Object around(ProceedingJoinPoint joinPoint, SystemOperLog systemOperLog){
        SysOperLog sysOperLog = new SysOperLog();
        try {
            //1、模块标题： 通过SysOperLog注解对象携带
            sysOperLog.setTitle(systemOperLog.title());
            //2、业务类型：crud  通过SysOperLog注解对象携带
            sysOperLog.setBusinessType(String.valueOf(systemOperLog.bussinessType().getType()));
            //3、目标方法： 通过joinpoint获取
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            String className = signature.getMethod().getDeclaringClass().getName();
            String methodName = signature.getMethod().getName();
            sysOperLog.setMethod(className+"."+methodName);

            //4、请求方式： 可以通过request对象获取
            // 每次请求 springboot都会将request对象存到RequestContextHolder中共享
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = requestAttributes.getRequest();

            String requestMethod = request.getMethod();
            sysOperLog.setRequestMethod(requestMethod);
            //5、操作类别：通过SysOperLog注解对象携带
            sysOperLog.setOperatorType(systemOperLog.operType().name());
            //6、操作人： 通过登录拦截器获取(将ThreadLocal共享数据的对象提取到manager和当前模块都可以访问到的地方)
            SysUser sysUser = AuthContextUtil.get();
            sysOperLog.setOperName(sysUser.getName());
            //7、访问的接口的路径：可以通过request对象获取
            String requestURL = request.getRequestURL().toString();
            sysOperLog.setOperUrl(requestURL);
            //8、获取操作人的ip地址：request  通过课件中准备的工具类解析request获取ip地址
            String ipAddress = IpUtil.getIpAddress(request);
            sysOperLog.setOperIp(ipAddress);
            //12、接口访问的时间：
            sysOperLog.setOperTime(new Date());
            //1、获取目标方法的参数列表
            Object[] args = joinPoint.getArgs();
            //9、请求参数： 前端不一定提交了请求参数、也可能提交的是上传的文件  可以通过SysOperLog注解的属性判断是否需要保存
            if (systemOperLog.isSaveParams()){
                sysOperLog.setOperParam(JSON.toJSONString(args));
            }
            //2、传入参数列表 执行目标方法  得到目标方法的结果
            Object result = joinPoint.proceed(args);
            //3、返回执行结果
            //10、响应结果：后端处理后不一定需要返回结果、也可能返回的是文件   可以通过SysOperLog注解的属性判断是否需要保存
            if (systemOperLog.isSaveResult()){
                sysOperLog.setJsonResult(JSON.toJSONString(result));
            }
            //11、接口的执行状态是否异常：
            sysOperLog.setStatus(0);
            return result;
        } catch (Throwable e) {
            sysOperLog.setStatus(1);
            sysOperLog.setErrorMsg(null);
            throw new GuiguException(ResultCodeEnum.ERROR,e);
        } finally {
             /*
                spring task 是springcontext包中提供的定时任务、自动缓存管理、异步工具
             */
            //保存操作日志
            service.save(sysOperLog);
        }

    }

}
